home *** CD-ROM | disk | FTP | other *** search
-
- /**************************************************************************************
-
- FILENAME
- Exceptions.h
-
- DESCRIPTION
- A collection of routines to handle assertions and exceptions.
-
- COPYRIGHT
- Copyright © Apple Computer, Inc. 1989-1993
- All rights reserved.
-
- ROUTINES
- EXTERNALS
- dprintf
-
- MACROS
- EXTERNALS
- SetExceptionOption - resumeLabelsOn / resumeLabelsOff
- check
- ncheck
- check_action
- ncheck_action
- require
- nrequire
- require_action
- nrequire_action
- retry
- resume
-
-
- NOTE
- To keep code size down, use these routines and macros with the C compiler option
- -b2 or -b3. This will eliminate duplicate strings.
- NOTE
- For more information on exception handling please refer to the book "Object-
- oriented Software Construction" by Bertrand Meyer, Prentice Hall International.
- Also, read the _develop_ article on Exception Handling by Sean Parent.
-
- **************************************************************************************/
-
- #ifndef __EXCEPTIONS__
- #define __EXCEPTIONS__
-
- /**************************************************************************************
-
- INCLUDES
-
- **************************************************************************************/
-
- #ifndef __TYPES__
- #include <Types.h>
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- CONSTANTS AND CONTROL
-
- **************************************************************************************/
-
- #define DEBUGOFF 0
- #define DEBUGWARN 1
- #define DEBUGON 2
- #define DEBUGFULL 3
-
- #ifndef DEBUGLEVEL
- #define DEBUGLEVEL DEBUGWARN
- #endif DEBUGLEVEL
-
- /*
- Declare a prototype that is used to ensure proper use of semicolons.
- */
-
- void PlaceHolder(void);
-
- /*
- These macros are used to control the insertion of the resume labels. If off
- then you can have multible requires sharing the same label but cannot resume.
- */
-
- #define resumeLabelsOn(exception) resume_ ## exception:
- #define resumeLabelsOff(exception)
-
- /*
- When using SetExceptionOption do not put a ; or anything other than a comment
- after the macro.
- */
-
- /* We disable resume labels, and the SetExceptionOption macro because they are
- not ANSI C happy */
- #define resumeLabel(exception)
- #define SetExceptionOption(option)
-
- /*
- trace and notrace are used for the traceOption for dprintf. If trace is used then
- the actual behavior cn be controled from Macsbug. The macros in Macsbug are traceGo
- and traceBreak. traceGo is the default and execution will continue after the break.
- If traceBreak is used then execution will halt.
- */
-
- #define trace "\p;dprintf;doTrace"
- #define notrace "\p;dprintf;mnop"
-
- /*
- traceon and debugon are used to test for options. For example:
-
- #if traceon
- dprintf(trace, "Foo Happened %d", 10);
- #endif
-
- #if debugon
- dprintf(notrace, "Foo Happened %d", 10);
- #endif
- */
-
-
- #define traceon ((DEBUGLEVEL > DEBUGWARN) && defined(TRACEON))
- #define debugon (DEBUGLEVEL > DEBUGWARN)
-
- /*<FF>*/
- /**************************************************************************************
-
- ROUTINE
- dprintf
-
- DESCRIPTION
- dprintf is used like printf only the information is displayed in MacsBug.
- traceOption is used to specify whether the execution should continue after the
- break or not. Of the standard printf convertion characters only "n" is not
- supported. In addition to the standard characters, the following are defined:
-
- b Boolean. Outputs either true or false.
- l Point. Used like the d option. (the l is for location.)
- L point*. Used like the f option. (the L is for Location.)
- F Fixed point number. Used like the f option.
- T Fract number. Used like the f option.
- r Rect*. Displays the rect with each piece seperated by ", ". Used like the
- d option.
- R rectangle*. Similar to r but for fixed point rectangles. Used like the
- f option.
- M mapping. Displays the mapping seperated by ", " and "\n". The third column
- is displayed as fract. Used like the f option.
-
- dprintf requires that the MacsBug dcmd dprintf is present.
-
- EXAMPLE
- The code:
-
- dprintf(notrace, "This is a Fract: %.8T", 0x70000000);
-
- Will display:
-
- This is a Fract: 1.75000000
-
- See the MPW C Reference for more information on printf.
-
- **************************************************************************************/
-
- void dprintf(StringPtr traceOption, char theFormat[], ...) = { 0xABFF, 0x594F };
-
- /*<FF>*/
- /**************************************************************************************
-
- ROUTINE check_dprintf
-
- DESCRIPTION
- if assertion is non-zero then assertion is returned. Otherwise the dprintf is
- invoked and zero is returned.
-
- Echo " ∂n∂
- PROC ∂n∂
- MOVE.L (SP)+,D0 ; Pop value into D0 ∂n∂
- BNE.S @1 ; If !0 then branch ∂n∂
- DC.W $ABFF ; DebugStr ∂n∂
- SUBQ #4,SP ; Fix stack for debugStr ∂n∂
- CLR.W D0 ; result is zero ∂n∂
- @1 SUBQ #4,SP ; Fix stack for pop ∂n∂
- ENDPROC ∂n∂
- END ∂n∂
- " | Asm -l
-
- **************************************************************************************/
-
- void* check_dprintf(void* assertion, StringPtr traceOption, char theFormat[], ...) =
- { 0x201F, 0x6606, 0xABFF, 0x594F, 0x4240, 0x594F };
-
- /*<FF>*/
- /**************************************************************************************
-
- ROUTINE checkpos_dprintf
-
- DESCRIPTION
- if assertion is non-zero then assertion is returned. Otherwise the dprintf is
- invoked and zero is returned.
-
- Echo " ∂n∂
- PROC ∂n∂
- MOVE.L (SP)+,D0 ; Pop value into D0 ∂n∂
- BGE.S @1 ; If >= 0 then branch ∂n∂
- DC.W $ABFF ; DebugStr ∂n∂
- SUBQ #4,SP ; Fix stack for debugStr ∂n∂
- CLR.W D0 ; result is zero ∂n∂
- @1 SUBQ #4,SP ; Fix stack for pop ∂n∂
- ENDPROC ∂n∂
- END ∂n∂
- " | Asm -l
-
- **************************************************************************************/
-
- void* checkpos_dprintf(void* assertion, StringPtr traceOption, char theFormat[], ...) =
- { 0x201F, 0x6C06, 0xABFF, 0x594F, 0x4240, 0x594F };
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- check
-
- DESCRIPTION
- If DEBUGON or DEBUGFULL are defined then check will test the assertion and if it
- failes break to the Debugger and print the assertion. If DEBUGON or DEBUGFULL are
- not defined then check does nothing.
-
- EXAMPLE
- check is very useful for testing preconditions. In the below example DoStuff is
- defined to take a handle to some object. It is a precondition of DoStuff that the
- handle must not be nil (and therefore is the callers resposibility to ensure that
- it is not.
-
- void DoStuff(Handle h)
- {
- check(h);
- (FooType)(*h)->fooField = 0;
- }
-
- Durring testing, however, if DEBUGFULL where defined and a caller did not live up
- to the contract by passing in a nil handle then a break would occure with the
- following:
-
- Assertion "h" Failed
- File: FooDisk:FooFile
- Line: 3
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define check(assertion) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed", #assertion); \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define check(assertion) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, __FILE__, __LINE__); \
- } \
- } else PlaceHolder()
-
- #else
-
- #define check(assertion)
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- ncheck
-
- DESCRIPTION
- ncheck is the same as check only it requires that the assertion is false.
-
- EXAMPLE
- ncheck is very useful for testing results when no recovery can be taken even if
- the action fails. This is most commen durring clean up.
-
- void DisposeStuff(shape theShape)
- {
- DisposeShape(theShape);
- ncheck(GetError());
- }
-
- If DEBUGFULL where defined and for some reason the dealocation failed then the
- following would be reported:
-
- Assertion "!(GetError() [= -108])" Failed
- File: FooDisk:FooFile
- Line: 3
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define ncheck(assertion) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed", \
- #assertion, __privateAssertion); \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define ncheck(assertion) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, __privateAssertion, __FILE__, __LINE__); \
- } \
- } else PlaceHolder()
-
- #else
-
- #define ncheck(assertion)
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- check_action
-
- DESCRIPTION
- If DEBUGON or DEBUGFULL are defined then check_action will test the assertion and
- if it failes break to the Debugger and print the assertion then execute the
- statement. If DEBUGON or DEBUGFULL are not defined then check_action does nothing.
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define check_action(assertion, action) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed", #assertion); \
- { action } \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define check_action(assertion, action) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, __FILE__, __LINE__); \
- { action } \
- } \
- } else PlaceHolder()
-
- #else
-
- #define check_action(assertion, action)
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- ncheck_action
-
- DESCRIPTION
- If DEBUGON or DEBUGFULL are defined then check_action will evaluate the assertion
- and if it evaluates to non-zero break to the Debugger and print the assertion then
- execute the statement. If DEBUGON or DEBUGFULL are not defined then ncheck_action
- does nothing.
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define ncheck_action(assertion, action) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed", \
- #assertion, __privateAssertion); \
- { action } \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define ncheck_action(assertion, action) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, __privateAssertion, __FILE__, __LINE__); \
- { action } \
- } \
- } else PlaceHolder()
-
- #else
-
- #define ncheck_action(assertion, action)
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- require
-
- DESCRIPTION
- require tests the assertion just as check does (with a break into the Debugger if
- the assertion failed and DEBUGON or DEBUGFULL was defined). If the assertion fails
- then a goto exception is executed.
-
- EXAMPLE
- OSErr DoStuff(void)
- {
- FooTypeHandle hFoo;
- BananaTypeHandle hBanana;
-
- hFoo = NewHandle(sizeof(FooType));
- require(hFoo, NewHandle_hFoo);
-
- hBanana = NewHandle(&hBanana, sizeof(BananaType));
- require(hBanana, NewHandle_hBanana);
-
- return(noErr);
-
- NewHandle_hBanana:
- DisposHandle(hFoo);
- NewHandle_hFoo:
- return(memFullErr);
- }
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define require(assertion, exception) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed\n" \
- "Exception \"%s\" Raised", \
- #assertion, #exception); \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define require(assertion, exception) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed\n" \
- "Exception \"%s\" Raised\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, #exception, __FILE__, __LINE__); \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #else
-
- #define require(assertion, exception) \
- if (true) { \
- if (assertion) ; \
- else { \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- nrequire
-
- DESCRIPTION
- nrequire is the same as require except that it insures that assertion is false.
-
- EXAMPLE
- OSErr DoStuff(void)
- {
- OSErr theError;
- FooTypeHandle hFoo;
- BananaTypeHandle hBanana;
-
- theError = MNewHandle(&hFoo, sizeof(FooType));
- nrequire(theError, MNewHandle_hFoo);
-
- theError = MNewHandle(&hBanana, sizeof(BananaType));
- nrequire(theError, MNewHandle_hBanana);
-
- return(noErr);
-
- MNewHandle_hBanana:
- (void)MDisposHandle(&hFoo);
- MNewHandle_hFoo:
- return(theError);
- }
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define nrequire(assertion, exception) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed\n" \
- "Exception \"%s\" Raised", \
- #assertion, __privateAssertion, #exception); \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define nrequire(assertion, exception) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed\n" \
- "Exception \"%s\" Raised\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, __privateAssertion, #exception, __FILE__, \
- __LINE__); \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #else
-
- #define nrequire(assertion, exception) \
- if (true) { \
- if (assertion) { \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- require_action
-
- DESCRIPTION
- require_action is identical to require except if the assertion fails then action
- is executed.
-
- EXAMPLE
- OSErr DoStuff(void)
- {
- FooTypeHandle hFoo;
- BananaTypeHandle hBanana;
- OSErr theError;
-
- hFoo = NewHandle(sizeof(FooType));
- require_action(hFoo, NewHandle_hFoo, theError = MemError(););
-
- hBanana = NewHandle(&hBanana, sizeof(BananaType));
- require_action(hBanana, NewHandle_hBanana, theError = MemError(););
-
- return(noErr);
-
- NewHandle_hBanana:
- DisposHandle(hFoo);
- NewHandle_hFoo:
- return(theError);
- }
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define require_action(assertion, exception, action) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed\n" \
- "Exception \"%s\" Raised", \
- #assertion, #exception); \
- { action } \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define require_action(assertion, exception, action) \
- if (true) { \
- if (assertion) ; \
- else { \
- dprintf(notrace, "Assertion \"%s\" Failed\n" \
- "Exception \"%s\" Raised\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, #exception, __FILE__, __LINE__); \
- { action } \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #else
-
- #define require_action(assertion, exception, action) \
- if (true) { \
- if (assertion) ; \
- else { \
- { action } \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- nrequire_action
-
- DESCRIPTION
- nrequire_action is the same as nrequire except it executes action when the
- assertion is true.
-
- EXAMPLE
- void *DoStuff(void)
- {
- OSErr theError;
- FooTypeHandle hFoo;
- BananaTypeHandle hBanana;
- void * result;
-
- theError = GetSpecialHandle(&hFoo);
- nrequire_action(theError, GetSpecialHandle, result = nil);
-
- result = (*hFoo)->theStuff;
-
- // Just let if fall through in this contrived example
-
- GetSpecialHandle:
- return(result);
- }
-
- **************************************************************************************/
-
- #if DEBUGLEVEL==DEBUGON
-
- #define nrequire_action(assertion, exception, action) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed\n" \
- "Exception \"%s\" Raised", \
- #assertion, __privateAssertion, #exception); \
- { action } \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #elif DEBUGLEVEL==DEBUGFULL
-
- #define nrequire_action(assertion, exception, action) \
- if (true) { \
- void* __privateAssertion = (void*)(assertion); \
- \
- if (__privateAssertion) { \
- dprintf(notrace, "Assertion \"!(%s [= 0x%08X])\" Failed\n" \
- "Exception \"%s\" Raised\n" \
- "File: %s\n" \
- "Line: %d", \
- #assertion, __privateAssertion, #exception, __FILE__, \
- __LINE__); \
- { action } \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #else
-
- #define nrequire_action(assertion, exception, action) \
- if (true) { \
- if (assertion) { \
- { action } \
- goto exception; \
- resumeLabel(exception); \
- } \
- } else PlaceHolder()
-
- #endif
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- retry
-
- DESCRIPTION
- retry is used to start a routine over if the routine fails. Variable initialization
- is not redone so variables can be changed to retry the routine under different
- circumstances. Routine parameters should not be changed or the routine will no
- longer be fulfilling its contract.
-
- EXAMPLE
- OSErr DoStuff(void)
- {
- OSErr theError;
- Handle h;
- Int16 iSize;
- Int16 iPieces = kGoodNumber;
- Int16 i;
-
- start:
-
- iSize = kFullSize / iPieces;
-
- for (i = 0; i <= iPieces; i++) {
- theError = MNewHandle(&h, iSize);
- nrequire(theError, MNewHandle);
- .
- .
- .
- theError = MDisposeHandle(&h);
- ncheck(theError);
- }
-
- return(noErr);
-
- MNewHandle:
-
- (void)MDisposeHandle(&h);
- iPieces += kMore;
-
- if (iPieces < kMaxPieces)
- retry;
- else
- return(theError);
- }
-
- **************************************************************************************/
-
- #define retry \
- if (true) { \
- goto start; \
- } else PlaceHolder()
-
-
- /*<FF>*/
- /**************************************************************************************
-
- MACRO
- resume
-
- DESCRIPTION
- resume is used to resume execution at the point where an exception occured. Below
- is a very contrived example of it's use. Note that this example also shows how
- to handle an exception in an exception. Although this is rarly neccisary sometimes
- it can be useful but make sure that it is clearly commented.
-
- EXAMPLE
- Boolean DoStuff(Boolean ***theSettings)
- {
- *theSettings = (Boolean **)GetResource('STIN', 128);
- require(*theSettings, GetResource);
-
- // Execution will resume here
-
- return(***theSettings);
-
- GetResource:
- *theSettings = (Boolean **)NewHandle(sizeof(Boolean));
- require(*theSettings, NewHandle);
- *theSettings = false;
- resume(GetResource);
-
- // Secondary exceptions
-
- NewHandle:
- return(false);
- }
-
- **************************************************************************************/
-
-
- #define resume(exception) \
- if (true) { \
- goto resume_ ## exception; \
- } else PlaceHolder()
-
-
- /*<FF>*/
- /*************************************************************************************/
-
- #endif